#pragma once

#include <Windows.h>
#include "ThreadPool.h"
#include <string>

namespace AsyncThread::Windows {

	// 最简单的互斥量自动释放器（函数内部使用）
	class AutoMutex {
	public:
		explicit AutoMutex(HANDLE hMutex) : m_mutex(hMutex) {
			WaitForSingleObject(m_mutex, INFINITE);  // 进入时自动加锁
		}
		~AutoMutex() {
			ReleaseMutex(m_mutex);  // 退出作用域时自动解锁
		}
	private:
		HANDLE m_mutex;
	};

	class CrossMutex {
	public:
		CrossMutex(const std::string uuid, const std::string& name, bool isGlobal = false)
			:mClassId("d2e4a1b3-5c6d-4f7a-8b9c-0d1e2f3a4b5c"),
			mMutex(OpenOrCreateMutex(MakeName(uuid, name, isGlobal))),
			mUnlockMutex(OpenOrCreateMutex(MakeName(uuid, "unlock_mutex", isGlobal))),
			mLockMutex(OpenOrCreateMutex(MakeName(uuid, "lock_mutex", isGlobal))),
			mLockThread(1)
		{
		}

		~CrossMutex() {
			if (mMutex != nullptr && mMutex != INVALID_HANDLE_VALUE) {
				::CloseHandle(mMutex);
				mMutex = nullptr;
			}
			if (mUnlockMutex != nullptr && mUnlockMutex != INVALID_HANDLE_VALUE) {
				::CloseHandle(mUnlockMutex);
				mUnlockMutex = nullptr;
			}
			if (mLockMutex != nullptr && mLockMutex != INVALID_HANDLE_VALUE) {
				::CloseHandle(mLockMutex);
				mLockMutex = nullptr;
			}
		}

		DWORD Lock(DWORD dwMillisecond = INFINITE)
		{
			// 保护 Lock 操作队列，保证一定只有一个线程在 WaitForSingleObject
			AutoMutex lockMutex(mLockMutex);

			if (dwMillisecond != INFINITE)
			{
				// 等待线程池释放互斥锁，防止重入
				// 拿到互斥量的所有权，即使其他线程 ReleaseMutex 了，也不会影响当前线程的 WaitForSingleObject
				auto status = ::WaitForSingleObject(mMutex, dwMillisecond);

				if (status == WAIT_TIMEOUT)
				{
					// 如果等待超时，直接返回
					return status;
				}
				else if (status == WAIT_FAILED)
				{
					DWORD dwError = ::GetLastError();
					throw std::system_error(
						std::error_code(dwError, std::system_category()),
						"WaitForSingleObject failed"
					);
				}
			}
			else {
				// 等待线程池释放互斥锁，防止重入
				// 拿到互斥量的所有权，即使其他线程 ReleaseMutex 了，也不会影响当前线程的 WaitForSingleObject
				::WaitForSingleObject(mMutex, INFINITE);
			}

			// 禁用所有 Unlock 操作，直到 Lock 操作完成
			AutoMutex unlockMutex(mUnlockMutex);

			// 释放锁，交给线程池持有
			::ReleaseMutex(mMutex);

			auto status = mLockThread.Enqueue([this]() {
				// 线程池持有锁
				auto status = ::WaitForSingleObject(mMutex, 0);

				if (status == WAIT_FAILED)
				{
					DWORD dwError = ::GetLastError();
					throw std::system_error(
						std::error_code(dwError, std::system_category()),
						"WaitForSingleObject failed"
					);
				}

				return status;
				}).get();

			return status;
		}

		void Unlock()
		{
			// 保护操作队列
			AutoMutex lock(mUnlockMutex);

			mLockThread.Enqueue([this]() {
				::ReleaseMutex(mMutex);
				});
		}

	private:
		static HANDLE CreateTempMutex() {
			// 创建未命名的互斥量（安全属性为NULL，初始无所有者）
			HANDLE hMutex = ::CreateMutexW(
				NULL,       // 默认安全属性
				FALSE,      // 初始无所有者
				NULL        // 未命名互斥量
			);

			// 更精确的错误检查
			if (hMutex == NULL) {
				DWORD dwError = ::GetLastError();
				throw std::system_error(
					std::error_code(dwError, std::system_category()),
					"CreateMutex failed"
				);
			}

			return hMutex;
		}

		std::string MakeName(const std::string& uniqueId, const std::string& name, bool isGlobal)
		{
			if (isGlobal)
			{
				return std::string("Global\\") + uniqueId + "_" + mClassId + "_" + name;
			}
			else
			{
				return std::string("Local\\") + uniqueId + "_" + mClassId + "_" + name;
			}
		}

		static HANDLE OpenOrCreateMutex(const std::string& name)
		{
			// 1. 先尝试打开已有互斥量
			HANDLE hMutex = OpenMutexA(
				MUTEX_ALL_ACCESS,
				FALSE,
				name.c_str());

			// 2. 如果打开失败且错误是对象不存在，则尝试创建
			if (hMutex == nullptr && GetLastError() == ERROR_FILE_NOT_FOUND)
			{
				// 创建新互斥量
				hMutex = CreateMutexA(
					NULL,
					false,
					name.empty() ? nullptr : name.c_str());

				// 3. 检查创建结果
				if (hMutex == nullptr)
				{
					throw std::runtime_error(
						"Failed to create mutex. Error code: " +
						std::to_string(GetLastError()));
				}
			}
			else if (hMutex == nullptr)
			{
				// 打开失败但不是因为对象不存在
				throw std::runtime_error(
					"Failed to open mutex. Error code: " +
					std::to_string(GetLastError()));
			}

			return hMutex;
		}

		ThreadPool mLockThread;
		HANDLE mMutex;
		HANDLE mUnlockMutex;
		HANDLE mLockMutex;
		std::string mClassId;
	};
}